﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Services;
using XNAColor = Microsoft.Xna.Framework.Color;
using MainGame.Menu;
using MainGame.GameSound;

namespace MainGame.GamePlay
{
  /// <summary>
  /// Disegna lo stato offerto dal servizio IBoardState e raccoglie e segnala i click
  /// sulle entitá disegnate
  /// </summary>
  public class BoardRenderer : Microsoft.Xna.Framework.DrawableGameComponent, IBoardRenderer
  {
    public class CardRender
    {
      public Card card;
      public Vector2 coordPosition;
      public bool isAnimated;
      public float depth = CARD_DEPTH;
      public bool showBack;
      public int NumSlot;
    }

    public class TableSlot
    {
      public Rectangle slotRectangle;
      public bool isOccupied;
    }

    public enum RenderState
    {
      Intro,
      Input,
      Animation,
      Outro,
      Blurred
    }

    static readonly int CARD_WIDTH = 105;
    static readonly int CARD_HEIGHT = 150;
    static readonly float CARD_DEPTH = 0.8f;
    static readonly int CARDS_SPACING = 10;

    private List<CardRender> cards;
    private List<CardRender> ExtractedCards;
    private int[] tableCardStacks = new int[52];

    // Compatibilità
    public bool Hints = true, Outro = false;

    private int tableCardsNum = 8;
    private TableSlot[] tableSlots;
    private TableSlot packSlot, extractedSlot;
    public RenderState renderState;
    IBoardState board_service;

    public BoardRenderer(Game game)
      : base(game)
    {
      UpdateOrder = (int)ComponentOrders.RENDERER;
      DrawOrder = (int)MenuDepths.GameLayer;

      game.Services.AddService(typeof(IBoardRenderer), this);
    }

    protected override void Dispose(bool disposing)
    {
      Game.Services.RemoveService(typeof(IBoardRenderer));
      base.Dispose(disposing);
    }

    public override void Initialize()
    {
      // Load services
      board_service = Game.Services.GetService(typeof(IBoardState)) as IBoardState;
      var input_services = Game.Services.GetService(typeof(IInputManager)) as IInputManager;
      input_services.OnTap += new InputManager.Tap(input_services_OnTap);
      input_services.OnDrag += new InputManager.Tap(input_services_Drag);

      // If there's a saved game, load the difficulty of the saved game
      if (Game.GetContext().currentGame != null)
        Game.GetContext().currentConfig.difficulty = Game.GetContext().currentGame.difficulty;
      
      if (Game.GetContext().currentConfig.difficulty == GameDifficulty.Hard) tableCardsNum = 6;

      // Setup table positions
      tableSlots = new TableSlot[tableCardsNum];
      var screenCenter = Game.GraphicsDevice.Viewport.Bounds.Center;
      Point startingPos = new Point(screenCenter.X - CARD_WIDTH - CARDS_SPACING, screenCenter.Y - CARD_HEIGHT - CARDS_SPACING);
      for (int j = 0; j < tableSlots.Count(); ++j)
        tableSlots[j] = new TableSlot()
        {
          slotRectangle = new Rectangle((int)startingPos.X + ((CARD_WIDTH + CARDS_SPACING) * (j % (tableCardsNum / 2))), (int)startingPos.Y + ((CARD_HEIGHT + CARDS_SPACING) * (j / (tableCardsNum / 2))), CARD_WIDTH, CARD_HEIGHT),
          isOccupied = false
        };

      // Setup cards
      cards = new List<CardRender>(); // Carte in Gioco
      SelectedCards = new List<CardRender>(); // Carte Selezionate
      ExtractedCards = new List<CardRender>(); // Carte Estratte
      RemovedCards = new List<CardRender>(); // Carte Rimosse

      // Pack position
      packSlot = new TableSlot()
      {
        slotRectangle = new Rectangle(50, Game.GraphicsDevice.Viewport.Bounds.Bottom - CARD_HEIGHT - 30, CARD_WIDTH, CARD_HEIGHT),
        isOccupied = true
      };
      //packPosition = new Point(30, Game.GraphicsDevice.Viewport.Bounds.Bottom - CARD_HEIGHT - 20);
      PackCardsNum = board_service.Pack.Cards.Count();
    
      // Extracted cards pack position
      extractedSlot = new TableSlot()
      {
        slotRectangle = new Rectangle(packSlot.slotRectangle.X, packSlot.slotRectangle.Y - (CARD_HEIGHT + 20), CARD_WIDTH, CARD_HEIGHT),
        isOccupied = false
      };

      // Mazzetti
      for (int i = 0; i < tableCardStacks.Count(); ++i)
        tableCardStacks[i] = 1;

      // Load Game
      renderState = RenderState.Intro;

      // If the current game is a loaded game, don't play intro
      if (Game.GetContext().currentGame != null)
      {
        renderState = RenderState.Input;
        UpdateCards();
      }

      base.Initialize();
    }

    // Initialize cards
    public void UpdateCards()
    {
      List<CardRender> OldCards = new List<CardRender>();
      for (int j = 0; j < cards.Count; j++)
      {
        OldCards.Add(cards[j]);
      }
      
      cards.Clear();

      
      for (int i = 0; i < board_service.Players.ElementAt(0).Cards.Count; i++)
      {
        var c = board_service.Players.ElementAt(0).Cards.ElementAt(i);
        var r = new Vector2(tableSlots[i].slotRectangle.X, tableSlots[i].slotRectangle.Y);

        cards.Add(new CardRender() { card = c, coordPosition = r, isAnimated = false, NumSlot = i });
        

        if (OldCards.Count > 0)
          for (int k = 0; k < OldCards.Count; k++)
          {
            if (OldCards[k].card.Value == c.Value && OldCards[k].card.Seed == c.Seed)
            { 
              cards[i].coordPosition = OldCards[k].coordPosition;
              cards[i].NumSlot = OldCards[k].NumSlot;
            }
          
          }      
      }

      ExtractedCards.Clear();

      for (int i = 0; i < board_service.ExtractedCards.Cards.Count; i++)
      {
        var c = board_service.ExtractedCards.Cards.ElementAt(i);
        var r = new Vector2(extractedSlot.slotRectangle.X, extractedSlot.slotRectangle.Y);
        ExtractedCards.Add(new CardRender() { card = c, coordPosition = r, isAnimated = false });
      }
    }

    SpriteBatch sprite_batch;
    Texture2D white, background;
    Texture2D Deck_Texture, texture_card, texture_back_card;
    SpriteFont BaveuseFont, font;
    Matrix screen_transform, smaller_screen_transform;
    RenderTarget2D smaller_render_target;

    protected override void LoadContent()
    {
      smaller_render_target = new RenderTarget2D(GraphicsDevice, GraphicsDevice.Viewport.Width / 3, GraphicsDevice.Viewport.Height / 3);
      smaller_screen_transform = Matrix.CreateScale((float)smaller_render_target.Width / GraphicsDevice.Viewport.Width,
          (float)smaller_render_target.Height / GraphicsDevice.Viewport.Height, 1.0f);

      var content = Game.Content;

      font = content.Load<SpriteFont>("small_font");
      BaveuseFont = content.Load<SpriteFont>("baveuse_small");

      white = content.Load<Texture2D>("white_pixel");
      texture_card = content.Load<Texture2D>(@"Images//Game//card_01");
      texture_back_card = content.Load<Texture2D>(@"Images//Decks//Back_" + Game.GetContext().currentConfig.chosenDeck);

      background = content.Load<Texture2D>(@"Images//Backgrounds//BG_" + Game.GetContext().currentConfig.chosenBackground);

      Deck_Texture = Game.Content.Load<Texture2D>(@"Images//Decks//Deck_" + Game.GetContext().currentConfig.chosenDeck);

      sprite_batch = Game.GetContext().SpriteBatch;
      screen_transform = Game.GetContext().ScreenTransform;

      base.LoadContent();
    }

    /// 
    /// INPUT
    /// 

    Point tap_position;
    bool tapping;
    /// <summary>
    /// Gestisce il Tapping
    /// </summary>
    /// <param name="tap_position">Posizione dove è avvenuto il Tap</param>
    void input_services_OnTap(Vector2 tap_position)
    {
      if (this.Enabled == false || renderState != RenderState.Input) return;

      this.tap_position = tap_position.ToPoint(screen_transform);

      tapping = true;

      // Tap sul Mazzo, estrae nuova carta
      if (packSlot.slotRectangle.Contains(this.tap_position))
      {
        // Deseleziona le carte in gioco
        board_service.Players.ElementAt(0).Cards.ForEach(c => c.Selected = false);
        ExtractedCards.ForEach(c => c.card.Selected = false);

        // Extract new cards
        PackCardsNum = board_service.Pack.Cards.Count();
        ClickPack(0, 1);

        // Play extracted card animation
        UpdateCards();
        if (PackCardsNum > 0)
        {
          ExtractedCards.Last().coordPosition.X = -200;
          PlayAnimation(Animation.MoveToExtracted.ToString(), ExtractedCards.Last());
          // Play a sound
          var random = new Random();
          var soundManager = Game.Services.GetService(typeof(ISoundManager<GamePlaySounds>)) as ISoundManager<GamePlaySounds>;
          soundManager.PlaySound((GamePlaySounds)random.Next((int)GamePlaySounds.PackPick1, (int)GamePlaySounds.PackPick3 + 1));

          PackCardsNum--;
        }
      }

      // Tap sulle carte estratte, seleziona la prima
      if (extractedSlot.slotRectangle.Contains(this.tap_position))
      {
        ClickExtracted(0);
        //PlayAnimation("Remove", AnimationCard);
      }
    }


    Point drag_position;
    Vector2 drag_direction = Vector2.Zero;
    bool drag = false;
    /// <summary>
    /// Gestisce il Drag
    /// </summary>
    /// <param name="press_position"></param>
    void input_services_Drag(Vector2 press_position)
    {
      //if (this.Enabled == false || Animation) return;
      if (this.Enabled == false || renderState != RenderState.Input) return;

      drag = true;

      Point new_drag_position = press_position.ToPoint(screen_transform);

      // Calcola la direzione del drag
      if (this.drag_position.X < new_drag_position.X) drag_direction.X = 1;
      if (this.drag_position.X > new_drag_position.X) drag_direction.X = -1;
      if (this.drag_position.X == new_drag_position.X) drag_direction.X = 0;

      if (this.drag_position.Y < new_drag_position.Y) drag_direction.Y = 1;
      if (this.drag_position.Y > new_drag_position.Y) drag_direction.Y = -1;
      if (this.drag_position.Y == new_drag_position.Y) drag_direction.Y = 0;

      this.drag_position = new_drag_position;

    }

    /// 
    /// UPDATE
    ///
    public override void Update(GameTime gameTime)
    {
      if (renderState == RenderState.Intro) Intro(gameTime); // INTRO

      base.Update(gameTime);
    }

    int IntroStep = 0, IntroCount = 0;
    public void Intro(GameTime gameTime)
    {
      // Step 1 - Estrate prime 6-8 carte dal mazzo
      if (IntroStep == 0)
      {
        if (IntroCount < tableCardsNum)
        {
          if (AnimationState == Animation.None)
          {
            ReplaceCards(0, board_service.Players.ElementAt(0).Cards.Count()); // Logica

            PackCardsNum = board_service.Pack.Cards.Count();

            // Setta Carta da Animare
            cards.Add(new CardRender() { card = board_service.Players.ElementAt(0).Cards.Last(), coordPosition = new Vector2(packSlot.slotRectangle.X, packSlot.slotRectangle.Y), isAnimated = false, NumSlot = IntroCount });
            cards[IntroCount].showBack = true;
            cards[IntroCount].depth = 1.0f; // Carta Animata in Primo Piano
            if (IntroCount > 0) cards[IntroCount - 1].depth = 0.8f; // Carte Precedenti in Secondo Piano

            PlayAnimation("Extract", cards[IntroCount]); // Setta Animazione ultima carta Estratta           
            IntroCount++;
          }

          for (int i = 0; i < cards.Count(); i++) cards[i].showBack = true;

        }
        else
        {
          if (AnimationState == Animation.None) // Aspetta che finisca l'animazione
          {
            IntroStep++; // Passa a Step Successivo
            IntroCount = 0; // Reset Count
          }
        }

      }

      // Step 2 - Scopre le carte
      if (IntroStep == 1)
      {
        if (IntroCount < cards.Count())
        {
          if (AnimationState == Animation.None)
          {
            cards[IntroCount].isAnimated = true;
            PlayAnimation("Turn", cards[IntroCount]); // Setta Animazione Scopre carta
            IntroCount++;
          }

        }
        else
        {
          if (AnimationState == Animation.None) // Aspetta che finisca l'animazione
          {
            IntroStep++; // Passa a Step Successivo
          }
        }
      }

      // Step 3 - Attiva Input
      if (IntroStep == 2)
      {
        UpdateCards();
        renderState = RenderState.Input; 
        if (Game.GetContext().currentConfig.tutorial && OnTutorial != null)
          OnTutorial();
      }

    } // Fine Intro

    ///
    /// DRAW
    ///

    /// <summary>
    /// Disegna la scena 
    /// </summary>
    /// <param name="gameTime"></param>
    public override void Draw(GameTime gameTime)
    {
      // Draw objects that follow the camera
      if (Enabled == false)
      {
        // Draw with a blur effect (secondary render target)
        GraphicsDevice.SetRenderTarget(smaller_render_target);
        GraphicsDevice.Clear(XNAColor.Black);
        sprite_batch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, null, null, null, null, smaller_screen_transform);
      }
      else
      {
        sprite_batch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, null, null, null, null, screen_transform);
      }

      DrawHintsSelectedCards();

      // Disegna il tavolo da gioco
      DrawBoard();

      // Disegna Fila di carte
      DrawPlayerBlock();

      // Disegna le carte estratte
      DrawExtractedCards();

      // Disegna Mazzo
      DrawPack();

      // ANIMAZIONI
      DrawAnimation();

      sprite_batch.End();

      if (Enabled == false)
      {
        GraphicsDevice.SetRenderTarget(null);
        sprite_batch.Begin();
        sprite_batch.Draw(smaller_render_target, new Rectangle(0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height), XNAColor.White);

        DrawOutro();

        sprite_batch.End();
      }

      base.Draw(gameTime);

      tapping = false;
      drag = false;

    } // Fine Draw

    XNAColor color_Background = XNAColor.White;
    /// <summary>
    /// Disegna il tavolo da gioco "Tavolo Verde"
    /// </summary>
    private void DrawBoard()
    {
      sprite_batch.Draw(background, new Rectangle(0, 0, 800, 480), null, color_Background, 0.0f, Vector2.Zero, SpriteEffects.None, 0.0f);

    } // Fine DrawBoard

    XNAColor ColorSlots = XNAColor.White;
    List<CardRender> SelectedCards;
    /// <summary>
    /// Disegna fila di carte in gioco sul tavolo
    /// </summary>
    /// <param name="player_index">Indice del giocatore</param>
    private void DrawPlayerBlock()
    {
      if (cards.Any() == false) return;

      for (int i = 0; i < cards.Count(); i++)
      {
        var c = cards[i];
        var r = new Rectangle((int)cards[i].coordPosition.X, (int)cards[i].coordPosition.Y, CARD_WIDTH, CARD_HEIGHT);
        if (c.isAnimated) r = AnimationRectangle;

        if (c.card.Selected)
        {
          // Calcola Lista delle carte Selezionate
          if (renderState == RenderState.Input)
          {
            int index = 0;
            SelectedCards.Clear();

            newCardsSlots[0] = -1;
            newCardsSlots[1] = -1;
            newCardsSlots[2] = -1;

            if (ExtractedCards.Count() > 0 && ExtractedCards.Last().card.Selected)
              SelectedCards.Add(ExtractedCards.Last());

            for (int j = 0; j < cards.Count(); j++)
              if (cards[j].card.Selected)
              {
                SelectedCards.Add(cards[j]);
                newCardsSlots[index++] = j;
              }
          }
          PlayAnimation("Select", c);
        }

        if (DrawCard(c, r))
          if (ClickPlayerCard != null)
          {
            ColorSlots.A = 0;

            // Memorizza il numero di carte estratte e nel mazzo per i controlli in Resume()
            ExtractCardsNum = board_service.ExtractedCards.Cards.Count;
            // Se l'ultima carta estratta è selezionata, decrementa le carte estratte disponibili
            if (ExtractedCards.Count > 0 && ExtractedCards.Last().card.Selected)
              ExtractCardsNum--;
            PackCardsNum = board_service.Pack.Cards.Count();
            ClickPlayerCard(0, i);
          }
      }

    } // Fine DrawPlayerBlock


    /// <summary>
    /// Disegna Carta da gioco su tavolo
    /// </summary>
    /// <param name="c">Carta da disegnare</param>
    /// <param name="r">Rettangolo che drescrive posizione e dimensione della carta</param>
    /// <returns>Restitiusce un bool che indica se la carta appena disegnata è anche Selezionata</returns>
    private bool DrawCard(CardRender c, Rectangle Card_Rectangle)
    {
      XNAColor ColorCard = XNAColor.White;

      int seed = 0;
      if (c.card.Seed == CardSeed.Hearts) { seed = 0; }
      if (c.card.Seed == CardSeed.Diamonds) { seed = 1; }
      if (c.card.Seed == CardSeed.Clubs) { seed = 2; }
      if (c.card.Seed == CardSeed.Spades) { seed = 3; }

      // Disegna carta selezionata sulle altre
      if (c.card.Selected)
      {
        ColorCard = XNAColor.Yellow;
      }

      // Carta

      if (!c.showBack)
        sprite_batch.Draw(Deck_Texture, Card_Rectangle, new Rectangle((Deck_Texture.Width / 13) * (c.card.Value - 1), (Deck_Texture.Height / 4) * (seed), Deck_Texture.Width / 13, Deck_Texture.Height / 4), ColorCard, 0.0f, Vector2.Zero, SpriteEffects.None, c.depth);
      else
        sprite_batch.Draw(texture_back_card, Card_Rectangle, new Rectangle(0, 0, texture_back_card.Width, texture_back_card.Height), ColorCard, 0.0f, Vector2.Zero, SpriteEffects.None, c.depth);

      Point pr = new Point(Math.Abs(drag_position.X), drag_position.Y);
      return tapping && Card_Rectangle.Contains(pr);

    } // Fine DrawCard

    float packRotation = 0.0f;
    XNAColor color_pack = XNAColor.White;
    /// <summary>
    /// Disegna il mazzo composto da n carte
    /// </summary>
    /// <param name="n">Numero di carte</param>
    private void DrawPack()
    {
      int n = PackCardsNum;// board_service.Pack.Cards.Count();
      int count = n; // Disegna al più 10 carte

      if (n > 10) count = 10 % n;
      if (n > 0)
        for (int i = 0; i < count; i++)
        {
          // Mazzo: n carte (dorso) sovrapposte e sfasate 
          sprite_batch.Draw(texture_back_card, new Rectangle(packSlot.slotRectangle.X + i, packSlot.slotRectangle.Y - i, CARD_WIDTH, CARD_HEIGHT), new Rectangle(0, 0, texture_back_card.Width, texture_back_card.Height), color_pack, packRotation, Vector2.Zero, SpriteEffects.None, 1.0f - (0.5f / (i + 1)));
        }

      // Disegna numero di carte del mazzo
      if (n > 0)
      {
        sprite_batch.DrawString(font, n.ToString(), new Vector2(155, 430), XNAColor.Black, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, 1.0f);
        sprite_batch.DrawString(font, n.ToString(), new Vector2(156, 431), XNAColor.White, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, 0.99f);
      }

    } // Fine DrawPack

    /// <summary>
    /// Disegna il mazzetto delle carte Estratte dal Mazzo non in gioco
    /// </summary>
    private void DrawExtractedCards()
    {
      int num = 0;
      if (ExtractedCards.Count() > 10) num = ExtractedCards.Count() - 10;

      // Disegna le ultime 10 Carte estratte, sfasate e sovrapposte
      for (int i = num; i < ExtractedCards.Count(); i++)
      {
        ExtractedCards[i].depth = 1.0f - (0.5f / ((i - num) + 1)); // Profondità della Carta
        Rectangle r = new Rectangle((int)(ExtractedCards[i].coordPosition.X) + (i - num), (int)(ExtractedCards[i].coordPosition.Y) - (i - num), CARD_WIDTH, CARD_HEIGHT);
        if (ExtractedCards[i].isAnimated)
        {
          ExtractedCards[i].depth = 1.0f;
          if (AnimationState == Animation.Turn)
          {
            r = AnimationRectangle;
            r.Offset((i - num), -(i - num));
          }
        }
        DrawCard(ExtractedCards[i], r);
      }

    } // Fine DrawExtracted

    public void DrawHintsSelectedCards()
    {
      if (!Game.GetContext().currentConfig.hints || AnimationState == Animation.Remove || AnimationState == Animation.Extract) return;

      Color HintsColor = Color.Lerp(Color.BlueViolet, Color.Transparent, 0.2f);

      for (int i = 0; i < cards.Count(); i++)
      {
        if (cards[i].card.Selected)
        {
          int SCV = cards[i].card.Value;

          for (int j = 0; j < cards.Count(); j++)
          {
            Rectangle hintsRect = new Rectangle(tableSlots[cards[j].NumSlot].slotRectangle.X - 5, tableSlots[cards[j].NumSlot].slotRectangle.Y - 5, tableSlots[cards[j].NumSlot].slotRectangle.Width + 10, tableSlots[cards[j].NumSlot].slotRectangle.Height + 10);
            if (cards.Count() > 0)
              if (cards[j].card.Value + SCV == 11) sprite_batch.Draw(white, hintsRect, null, HintsColor, 0.0f, Vector2.Zero, SpriteEffects.None, cards[j].depth - 0.01f);

            if (ExtractedCards.Count() > 0)
              if (ExtractedCards.Last().card.Value + SCV == 11)
              {
                hintsRect = new Rectangle(extractedSlot.slotRectangle.X - 5, (int)ExtractedCards.Last().coordPosition.Y - 5, ((int)ExtractedCards.Last().coordPosition.X - (int)extractedSlot.slotRectangle.X) + CARD_WIDTH + 10, extractedSlot.slotRectangle.Height + 10);
                sprite_batch.Draw(white, hintsRect, null, HintsColor, 0.0f, Vector2.Zero, SpriteEffects.None, ExtractedCards[0].depth - 0.01f);
              }

            if (SCV > 10)
            {
              CardSeed SCS = cards[i].card.Seed;

              for (int u = 0; u < cards.Count(); u++)
              {
                if (cards[u].card.Value > 10 && cards[u].card.Value != SCV && cards[u].card.Seed == SCS)
                {
                  for (int h = 0; h < cards.Count(); h++)
                  {
                    if (cards[h].card.Value > 10 && cards[h].card.Value != SCV && cards[h].card.Value != cards[u].card.Value && cards[h].card.Seed == SCS)
                    {
                      hintsRect = new Rectangle(tableSlots[cards[u].NumSlot].slotRectangle.X - 5, tableSlots[cards[u].NumSlot].slotRectangle.Y - 5, tableSlots[cards[u].NumSlot].slotRectangle.Width + 10, tableSlots[cards[u].NumSlot].slotRectangle.Height + 10);
                      sprite_batch.Draw(white, hintsRect, null, HintsColor, 0.0f, Vector2.Zero, SpriteEffects.None, cards[u].depth - 0.01f);

                      hintsRect = new Rectangle(tableSlots[cards[h].NumSlot].slotRectangle.X - 5, tableSlots[cards[h].NumSlot].slotRectangle.Y - 5, tableSlots[cards[h].NumSlot].slotRectangle.Width + 10, tableSlots[cards[h].NumSlot].slotRectangle.Height + 10);
                      sprite_batch.Draw(white, hintsRect, null, HintsColor, 0.0f, Vector2.Zero, SpriteEffects.None, cards[h].depth - 0.01f);
                    }
                  }
                  if (ExtractedCards.Count() > 0)
                    if (ExtractedCards.Last().card.Value > 10 && ExtractedCards.Last().card.Value != SCV && ExtractedCards.Last().card.Value != cards[u].card.Value && ExtractedCards.Last().card.Seed == SCS)
                    {
                      hintsRect = new Rectangle(extractedSlot.slotRectangle.X - 5, (int)ExtractedCards.Last().coordPosition.Y - 5, ((int)ExtractedCards.Last().coordPosition.X - (int)extractedSlot.slotRectangle.X) + CARD_WIDTH + 10, extractedSlot.slotRectangle.Height + 10);
                      sprite_batch.Draw(white, hintsRect, null, HintsColor, 0.0f, Vector2.Zero, SpriteEffects.None, ExtractedCards[0].depth - 0.01f);

                      hintsRect = new Rectangle(tableSlots[cards[u].NumSlot].slotRectangle.X - 5, tableSlots[cards[u].NumSlot].slotRectangle.Y - 5, tableSlots[cards[u].NumSlot].slotRectangle.Width + 10, tableSlots[cards[u].NumSlot].slotRectangle.Height + 10);
                      sprite_batch.Draw(white, hintsRect, null, HintsColor, 0.0f, Vector2.Zero, SpriteEffects.None, cards[u].depth - 0.01f);
                    }
                }
              }
            }
          }
        }
      }

      if (ExtractedCards.Count() > 0)
        if (ExtractedCards.Last().card.Selected)
        {
          int SCV = ExtractedCards.Last().card.Value;

          for (int j = 0; j < cards.Count(); j++)
          {
            Rectangle hintsRect = new Rectangle(tableSlots[j].slotRectangle.X - 5, tableSlots[j].slotRectangle.Y - 5, tableSlots[j].slotRectangle.Width + 10, tableSlots[j].slotRectangle.Height + 10);
            if (cards.Count() > 0)
              if (cards[j].card.Value + SCV == 11) sprite_batch.Draw(white, hintsRect, null, Color.BlueViolet, 0.0f, Vector2.Zero, SpriteEffects.None, cards[j].depth - 0.01f);

            //if (ExtractedCards.Count() > 0)
            //  if (ExtractedCards.Last().card.Value + SCV == 11)
            //  {
            //    hintsRect = new Rectangle(extractedSlot.slotRectangle.X - 5, (int)ExtractedCards.Last().coordPosition.Y - 5, ((int)ExtractedCards.Last().coordPosition.X - (int)extractedSlot.slotRectangle.X) + CARD_WIDTH + 10, extractedSlot.slotRectangle.Height + 10);
            //    sprite_batch.Draw(white, hintsRect, null, Color.BlueViolet, 0.0f, Vector2.Zero, SpriteEffects.None, ExtractedCards[0].depth - 0.01f);
            //  }

            if (SCV > 10)
            {
              CardSeed SCS = ExtractedCards.Last().card.Seed;

              for (int u = 0; u < cards.Count(); u++)
              {
                if (cards[u].card.Value > 10 && cards[u].card.Value != SCV && cards[u].card.Seed == SCS)
                {
                  for (int h = 0; h < cards.Count(); h++)
                  {
                    if (cards[h].card.Value > 10 && cards[h].card.Value != SCV && cards[h].card.Value != cards[u].card.Value && cards[h].card.Seed == SCS)
                    {
                      hintsRect = new Rectangle(tableSlots[u].slotRectangle.X - 5, tableSlots[u].slotRectangle.Y - 5, tableSlots[u].slotRectangle.Width + 10, tableSlots[u].slotRectangle.Height + 10);
                      sprite_batch.Draw(white, hintsRect, null, Color.BlueViolet, 0.0f, Vector2.Zero, SpriteEffects.None, cards[u].depth - 0.01f);

                      hintsRect = new Rectangle(tableSlots[h].slotRectangle.X - 5, tableSlots[h].slotRectangle.Y - 5, tableSlots[h].slotRectangle.Width + 10, tableSlots[h].slotRectangle.Height + 10);
                      sprite_batch.Draw(white, hintsRect, null, Color.BlueViolet, 0.0f, Vector2.Zero, SpriteEffects.None, cards[h].depth - 0.01f);
                    }
                  }
                }
              }
            }
          }
        }
    }


    ///
    /// ANIMAZIONI
    ///

    private enum Animation
    {
      None,
      Select,
      Extract,
      Turn,
      Remove,
      MoveToExtracted
    }


    Animation OldAnimationState = Animation.None;

    public void PlayAnimation(string a, CardRender c)
    {
      if (a == Animation.Select.ToString()) OldAnimationState = Animation.Select;
      if (a == Animation.Extract.ToString()) OldAnimationState = Animation.Extract;
      if (a == Animation.Turn.ToString()) OldAnimationState = Animation.Turn;
      if (a == Animation.Remove.ToString()) OldAnimationState = Animation.Remove;
      if (a == Animation.MoveToExtracted.ToString()) OldAnimationState = Animation.MoveToExtracted;

      if (AnimationState == Animation.None)
      {
        AnimationCard = c;
        AnimationState = OldAnimationState;
      }
    }

    Animation AnimationState = Animation.None;
    CardRender AnimationCard = null;
    Rectangle AnimationRectangle = new Rectangle(0, 0, 0, 0);

    // Gestore Animazioni
    public void DrawAnimation()
    {
      // Animazione deselezione carte
      if (newCardsResume) Resume();
      else DeSelectCard(AnimationCard); // DA POSIZIONARE MEGLIO

      if (AnimationState == Animation.None)
        return;

      AnimationCard.depth = 1.0f; // In Primo Piano

      if (AnimationState == Animation.Select) { SelectCard(AnimationCard); }
      if (AnimationState == Animation.Extract) { ExtractCard(AnimationCard); }
      if (AnimationState == Animation.Turn) { TurnCard(AnimationCard); }
      if (AnimationState == Animation.Remove) { RemoveCard(AnimationCard); }
      if (AnimationState == Animation.MoveToExtracted) { MoveToExtracted(AnimationCard); }

    }

    public void SelectCard(CardRender c)
    {
      int speed = 3;

      for (int i = 0; i < cards.Count(); i++)
      {
        c = cards[i];

        c.depth = 0.9f;

        if (c.card.Selected)
        {
          if (c.coordPosition.Y > tableSlots[c.NumSlot].slotRectangle.Y - 10)
            c.coordPosition.Y -= speed;
          else
          {
            renderState = RenderState.Input;
            AnimationState = Animation.None;
          }
        }
        else
        {
          if (c.coordPosition.Y < tableSlots[c.NumSlot].slotRectangle.Y)
            c.coordPosition.Y += speed;
          else
          {
            renderState = RenderState.Input;
            AnimationState = Animation.None;
          }
        }
      }

      if (SelectedCards.Count() > 1)
      {
        //PlayAnimation("Remove", c);
        if (SelectedCards.Count() == 3) { PlayAnimation("Remove", c); }
        if (SelectedCards.Count == 2 && (SelectedCards[0].card.Value + SelectedCards[1].card.Value == 11))
        {
          PlayAnimation("Remove", c);
        }
      }
      else
      {
        renderState = RenderState.Input;
        AnimationState = Animation.None;
      }
    } // Fine SelectCard


    public void DeSelectCard(CardRender c)
    {
      int speed = 3;
      // Carte sul tavolo
      for (int i = 0; i < cards.Count(); i++)
      {
        c = cards[i];
        if (!c.card.Selected && c.coordPosition.Y < tableSlots[c.NumSlot].slotRectangle.Y) c.coordPosition.Y += speed;
      }

      //// Carte Estratte
      if (ExtractedCards.Count() > 0)
        if (!(ExtractedCards.Last().card.Selected) && ExtractedCards.Last().coordPosition.Y < extractedSlot.slotRectangle.Y) ExtractedCards.Last().coordPosition.Y += speed;

    } // Fine DeSelectCard

    int extractCardStep = 0;
    Vector2 ExtractCardFinalPosition = Vector2.Zero, ExtractCardPosition = Vector2.Zero, track = Vector2.Zero;
    /// <summary>
    /// Animazione Estrazione di una carta dal mazzo
    /// </summary>
    /// <param name="c"></param>
    public void ExtractCard(CardRender c)
    {
      int speed = 50;

      // Step 0 - Calcola posizione di partenza e di destinazione
      if (extractCardStep == 0)
      {
        ExtractCardFinalPosition = new Vector2(tableSlots[cards.Count() - 1].slotRectangle.X, tableSlots[cards.Count() - 1].slotRectangle.Y);
        //ExtractCardPosition = new Vector2(packSlot.slotRectangle.X, packSlot.slotRectangle.Y); //new Vector2(packPosition.X, packPosition.Y);

        c.coordPosition = new Vector2(packSlot.slotRectangle.X, packSlot.slotRectangle.Y);

        extractCardStep++;

        // Play sound
        var rnd = new Random();
        var soundManager = Game.Services.GetService(typeof(ISoundManager<GamePlaySounds>)) as ISoundManager<GamePlaySounds>;
        soundManager.PlaySound((GamePlaySounds)rnd.Next((int)GamePlaySounds.PackPick1, (int)GamePlaySounds.PackPick3 + 1));
      }

      track = ExtractCardFinalPosition - c.coordPosition; // Calcola vettore spostamento

      // Step 1 - Sposta la carta fino allo slot di destinazione
      if (extractCardStep == 1)
      {
        if (c.coordPosition.X < ExtractCardFinalPosition.X)
        {
          // Applica spostamento
          track.Normalize();
          track *= speed;

          c.coordPosition.X += (int)track.X; // Cambia posizione
          c.coordPosition.Y += (int)track.Y;

          if (c.coordPosition.X > ExtractCardFinalPosition.X)
            c.coordPosition = ExtractCardFinalPosition;

          if (newCardsResume) c.depth = 1.0f;
          // DrawCard(AnimationCard, new Rectangle((int)c.coordPosition.X, (int)c.coordPosition.Y, CARD_WIDTH, CARD_HEIGHT));
          // Ombra
          sprite_batch.Draw(texture_card, new Rectangle((int)c.coordPosition.X + 10, (int)c.coordPosition.Y + 10, CARD_WIDTH, CARD_HEIGHT), new Rectangle(0, 0, CARD_WIDTH, CARD_HEIGHT), new XNAColor(0, 0, 0, 100), 0.0f, Vector2.Zero, SpriteEffects.None, 0.99f);
        }
        else
          extractCardStep++;
      }

      // Step 2 - Reset Valori
      if (extractCardStep == 2)
      {
        extractCardStep = 0;
        AnimationState = Animation.None;
        //if(renderState != RenderState.Intro) UpdateCards();
      }
    } // Fine DrawExtractCard

    int moveToExtractedStep = 0;
    public void MoveToExtracted(CardRender c)
    {
      int speed = 50;
      if (moveToExtractedStep == 0)
      {
        c.depth = 1.0f;
        c.isAnimated = true;
        c.showBack = true;
        c.coordPosition = new Vector2(packSlot.slotRectangle.X, packSlot.slotRectangle.Y);
        moveToExtractedStep++;
      }

      if (moveToExtractedStep == 1)
      {
        c.coordPosition.Y -= speed;
        if (c.coordPosition.Y < extractedSlot.slotRectangle.Y)
        {
          c.coordPosition.Y = extractedSlot.slotRectangle.Y;
          moveToExtractedStep++;
        }
      }

      if (moveToExtractedStep == 2)
      {
        AnimationState = Animation.None;
        c.isAnimated = false;
        PlayAnimation("Turn", c);
        moveToExtractedStep = 0;
      }
    }

    int turnCardStep = 0, CWOffset = 0;
    float flipPitch = 0.0f;
    public void TurnCard(CardRender c)
    {
      int speed = 13;

      // Step 0 - Set Valori Iniziali
      if (turnCardStep == 0)
      {
        turnCardStep++;
        c.isAnimated = true;

        // Play sound
        var soundManager = Game.Services.GetService(typeof(ISoundManager<GamePlaySounds>)) as ISoundManager<GamePlaySounds>;
        if (renderState == RenderState.Intro)
        {
          flipPitch += 0.1f;
          if (flipPitch > 1.0f) flipPitch = 1.0f;
          soundManager.PlaySound(GamePlaySounds.CardFlip4, soundManager.SfxVolume, flipPitch, 0.5f);
        }
        else
        {
          var rnd = new Random();
          soundManager.PlaySound((GamePlaySounds)rnd.Next((int)GamePlaySounds.CardFlip1, (int)GamePlaySounds.CardFlip5 + 1));
        }
      }

      // Step 1 - Gira Carta
      if (turnCardStep == 1)
      {
        AnimationRectangle = new Rectangle((int)c.coordPosition.X + CWOffset, (int)c.coordPosition.Y, CARD_WIDTH - (2 * CWOffset), CARD_HEIGHT);

        // Ombra
        sprite_batch.Draw(texture_card, new Rectangle(AnimationRectangle.X + 10, AnimationRectangle.Y + 10, AnimationRectangle.Width, AnimationRectangle.Height), new Rectangle(0, 0, CARD_WIDTH, CARD_HEIGHT), new XNAColor(0, 0, 0, 100), 0.0f, Vector2.Zero, SpriteEffects.None, 0.99f);

        CWOffset += speed;

        if (CWOffset > (2 * CARD_WIDTH) / 3) turnCardStep++;
      }

      // Step 2 - Reset Valori
      if (turnCardStep == 2)
      {
        c.showBack = !(c.showBack);
        c.isAnimated = false;
        CWOffset = 0;
        turnCardStep = 0;
        AnimationState = Animation.None;
      }

    } // Fine TurnCard

    List<CardRender> RemovedCards;
    int RemoveCardStep = 0;
    int[] newCardsSlots = new int[3];
    private void RemoveCard(CardRender c)
    {
      if (RemoveCardStep == 0)
      {
        renderState = RenderState.Animation;

        RemovedCards.Clear();
        for (int k = 0; k < SelectedCards.Count(); k++)
          RemovedCards.Add(SelectedCards[k]);

        RemovedCards.ForEach(card => card.depth = 1.0f);

        RemoveCardStep = 1;
      }

      int speed = 20;

      if (RemoveCardStep == 1)
      {
        // Muove le carte verso l'alto
        for (int i = 0; i < RemovedCards.Count(); i++)
        {
          if (RemovedCards[i].coordPosition.Y > -(CARD_HEIGHT * 2))
            RemovedCards[i].coordPosition.Y -= speed;
          //RemovedCards[i].depth = 1.0f;
        }

        // Controlla se tutte le carte da rimuovere sono state spostate
        if (RemovedCards.Count() > 0)
          if (RemovedCards[RemovedCards.Count() - 1].coordPosition.Y <= -CARD_HEIGHT + 1)
            RemoveCardStep = 2;

        //if (RemovedCards.Count() < 1)
        //{
        //  RemovedCards.Clear();

        //  for (int k = 0; k < SelectedCards.Count(); k++)
        //  {
        //    //newCardsSlots[k] = (int)SelectedCards[k].coordPosition.Y;
        //    RemovedCards.Add(SelectedCards[k]);
        //  }
        //}
      }

      if (RemoveCardStep == 2)
      {

        // Controlla che ci siano carte disponibili da estrarre
        int availableCards = ExtractCardsNum + PackCardsNum;
        if (availableCards < newCardsSlots.Count())
        {
          // Se non ci sono carte a sufficienza, aggiorna newCardsSlots
          for (int j = availableCards; j < newCardsSlots.Count(); ++j)
            newCardsSlots[j] = -1;
        }

        // Lancia le animazioni di replace delle carte
        if ((Game.GetContext().currentConfig.difficulty == GameDifficulty.Easy && ExtractCardsNum > 0) || PackCardsNum > 0)
        {
          AnimationState = Animation.None;
          newCardsResume = true;
          UpdateCards();

          for (int i = 0; i < newCardsSlots.Count(); i++)
          {
            if (newCardsSlots[i] != -1 && newCardsSlots[i] < cards.Count())
            {
              if (i < ExtractCardsNum)
              {
                Vector2 offset = new Vector2(extractedSlot.slotRectangle.X, extractedSlot.slotRectangle.Y);
                //if (ExtractedCards.Count() > 0)
                //  offset = ExtractedCards.Last().coordPosition;

                if (ExtractedCards.Count() > 10)
                {
                  offset.X += 10;
                  offset.Y -= 10;
                }
                else if (ExtractedCards.Count > 0)
                {
                  offset.X = ExtractedCards.Last().coordPosition.X + i;
                  offset.Y = ExtractedCards.Last().coordPosition.Y - i;
                }
                  
                cards[newCardsSlots[i]].coordPosition.X = offset.X;
                cards[newCardsSlots[i]].coordPosition.Y = offset.Y;
                cards[newCardsSlots[i]].depth = 1.0f - (0.001f * i);

                if (ExtractedCards.Count > 0)
                {
                  cards[newCardsSlots[i]].coordPosition.X = ExtractedCards.Last().coordPosition.X + i;
                  cards[newCardsSlots[i]].coordPosition.Y = ExtractedCards.Last().coordPosition.Y - i;
                }
              }
              else
                cards[newCardsSlots[i]].coordPosition.X = -200;

              if (Game.GetContext().currentConfig.difficulty != GameDifficulty.Easy/* && newCardsSlots[i] != -1*/)
                cards[newCardsSlots[i]].coordPosition.X = -200;
            }
          }
        }
        else
        {
          RemoveCardStep = 3;
        }
      }

      if (RemoveCardStep == 3)
      {
        // Reset Valori 
        newCardsSlots[0] = -1;
        newCardsSlots[1] = -1;
        newCardsSlots[2] = -1;

        RemoveCardStep = 0;
        RemovedCards.Clear();
        SelectedCards.Clear();
        newCardsResume = false;

        UpdateCards();

        PackCardsNum = board_service.Pack.Cards.Count;
        AnimationState = Animation.None;
        renderState = RenderState.Input;
        //if (PackCardsNum == 0 && ExtractCardsNum == 0 && cards.Count == 0)
        //  if (OnExit != null)
        //    OnExit();
      }
    } // Fine RemoveCard

    int ResumeStep = 0, PackCardsNum, ExtractCardsNum = 0, indexResume = 0;
    bool newCardsResume = false;
    public void Resume()
    {
      if (ResumeStep == 0)
      {
        if (AnimationState == Animation.None)
        {
          // setta i parametri per l'animazione
          ExtractCardPosition = new Vector2(packSlot.slotRectangle.X, packSlot.slotRectangle.Y);
          cards[newCardsSlots[indexResume]].showBack = true;
          ExtractCardFinalPosition = new Vector2(tableSlots[newCardsSlots[indexResume]].slotRectangle.X, tableSlots[newCardsSlots[indexResume]].slotRectangle.Y);

          // Controlla se la carta da estrarre proviene dal mazzo o dal mazzo di carte estratte
          if (ExtractCardsNum > 0 && Game.GetContext().currentConfig.difficulty == GameDifficulty.Easy)
          {
            ExtractCardPosition = new Vector2(extractedSlot.slotRectangle.X, extractedSlot.slotRectangle.Y);
            cards[newCardsSlots[indexResume]].showBack = false;
            ExtractCardsNum--;
          }
          else if (PackCardsNum > 0)
            PackCardsNum--;

          // procede con l'animazione
          cards[newCardsSlots[indexResume]].coordPosition = ExtractCardPosition;
          extractCardStep = 1;
          PlayAnimation("Extract", cards[newCardsSlots[indexResume]]);

          // Play sound
          var rnd = new Random();
          var soundManager = Game.Services.GetService(typeof(ISoundManager<GamePlaySounds>)) as ISoundManager<GamePlaySounds>;
          soundManager.PlaySound((GamePlaySounds)rnd.Next((int)GamePlaySounds.PackPick1, (int)GamePlaySounds.PackPick3 + 1));

          // Passa al prossimo step
          ResumeStep++;
        }
      }

      if (ResumeStep == 1)
      {
        // Esegue i cicli di animazione
        if (AnimationState == Animation.None)
        {
          if (AnimationCard.showBack)
          {
            AnimationCard = cards[newCardsSlots[indexResume]];
            AnimationCard.coordPosition = new Vector2(tableSlots[newCardsSlots[indexResume]].slotRectangle.X, tableSlots[newCardsSlots[indexResume]].slotRectangle.Y);

            PlayAnimation("Turn", AnimationCard);
          }
          ResumeStep++;
        }
      }

      if (ResumeStep == 2)
      {
        if (AnimationState == Animation.None)
        {
          ResumeStep = 0;
          cards[newCardsSlots[indexResume]].depth = CARD_DEPTH;
          indexResume++;
          if (indexResume == newCardsSlots.Count() || (newCardsSlots[indexResume] == -1) || (ExtractCardsNum + PackCardsNum <= 0))
          {
            indexResume = 0;
            newCardsResume = false;

            RemoveCardStep = 3;
            PlayAnimation("Remove", AnimationCard);
          }

        }
      }
    }

    int OutroTime = 0;
    public void DrawOutro()
    {
      if (!Outro) return;

      // Vittoria
      if (PackCardsNum == 0 && ExtractCardsNum == 0 && board_service.Players.ElementAt(0).Cards.Count == 0)
      { 
        if (OutroTime > 40 && OnExit != null) { OnExit(); Outro = false; }
      }
      else
      {
        // Sconfitta
        sprite_batch.DrawString(BaveuseFont, Locale.Strings.NoMoreMoves, new Vector2(62, 222), XNAColor.Black, 0.0f, Vector2.Zero, 2.5f, SpriteEffects.None, 0.99f);
        sprite_batch.DrawString(BaveuseFont, Locale.Strings.NoMoreMoves, new Vector2(60, 220), XNAColor.Red, 0.0f, Vector2.Zero, 2.5f, SpriteEffects.None, 1.0f);
      }

      OutroTime++;
      if (OutroTime >= 70) if (OnExit != null) { OnExit(); Outro = false; }
    }

    /// <summary>
    /// EVENTI
    /// </summary>

    public event ClickPlayerCard ClickPlayerCard;
    public event ClickBlockCard ClickBlockCard;
    public event ClickPack ClickPack;
    public event ClickExtracted ClickExtracted;
    public event ReleasePlayerCard ReleasePlayerCard;
    public event ReplaceCards ReplaceCards;
    public event OnExit OnExit;
    public event OnTutorial OnTutorial;
  }


}
